package com.ruoyi.flow.common.tools.fastdfs;

import com.ruoyi.flow.common.utils.PropertiesUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.FileInfo;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static com.google.common.base.Preconditions.checkState;

import java.io.*;

/**
 * FastDFS工具类，用于向FastDFS中上传文件、下载文件到服务器临时目录、删除文件。
 */
class InnerFastDFSApi {
    private static final Logger LOGGER = LoggerFactory.getLogger(InnerFastDFSApi.class);
    private TrackerServerPool pool = null;
    private int maxPoolSize = 30;
    private int minPoolSize = 10;
    private long waitTime = 2000000;
    private int port;
    private static final int DEFAULT_PORT = 8088;
    private static final String EMPTY_STRING = "";

    public int getMaxPoolSize() {
        return maxPoolSize;
    }

    public void setMaxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
    }

    public int getMinPoolSize() {
        return minPoolSize;
    }

    public void setMinPoolSize(int minPoolSize) {
        this.minPoolSize = minPoolSize;
    }

    public long getWaitTime() {
        return waitTime;
    }

    public void setWaitTime(long waitTime) {
        this.waitTime = waitTime;
    }

    InnerFastDFSApi() {
        // 获取端口
        PropertiesUtils pu = PropertiesUtils.load(TrackerServerPool.Builder.CONFIG_PATH);
        this.port = pu.getInt("http.tracker_http_port", DEFAULT_PORT);
        // 获取最大对象数
        int maxTotal = pu.getInt("max_total", maxPoolSize);
        setMaxPoolSize(maxTotal);
        //初始化对象池
        this.pool = TrackerServerPool.builder().maxPoolSize(getMaxPoolSize()).minPoolSize(getMinPoolSize()).waitTime(getWaitTime()).build();
    }

    /**
     * 将文件上传到FastDFS服务器
     *
     * @param file {@link File}
     * @return 上传成功后的远程文件信息，包括<i>远程组名</i>和<i>远程文件名</i>。通过这两个信息可以对远程文件进行下载、删除等操作。
     * @see FdfsFileInfo
     */
    public FdfsFileInfo upload(File file) {
        LOGGER.info("begin upload file {}，fileSize:{}", file.getName(), file.length());
        FileInputStream fis = null;
        TrackerServer trackerServer = null;
        try {
            /** 获取FastDFS服务器连接*/
            trackerServer = pool.takeTrackerServer();
            StorageClient client = createStorageClient(trackerServer);

            /** 封装文件信息 */
            String suffix = StringUtils.substringAfterLast(file.getName(), ".");
            NameValuePair[] meta_list = new NameValuePair[3];
            meta_list[0] = new NameValuePair("fileName", file.getName());
            meta_list[1] = new NameValuePair("fileExtName", suffix);
            meta_list[2] = new NameValuePair("fileLength", String.valueOf(file.length()));
            fis = new FileInputStream(file);
            byte[] file_buff = null;
            if (fis != null) {
                int len = fis.available();
                file_buff = new byte[len];
                fis.read(file_buff);
            }

            checkState(file.length() == file_buff.length, "{}文件大小与流大小不一致！文件大小{}，流大小{}", file.getName(), file.length(), file_buff.length);

            /** 文件上传 */
            String[] results = client.upload_file(file_buff, suffix, meta_list);

            if (results == null || results.length < 2) {
                return null;
            }
            String groupName = results[0];
            String remoteFileName = results[1];
            LOGGER.info("upload file {} Success!", file.getName());
            return new FdfsFileInfo(groupName, remoteFileName);
        } catch (FileNotFoundException e) {
            LOGGER.error("Uploaded to FastDFS file " + file.getName() + " not found .", e);
        } catch (IOException e) {
            LOGGER.error("FastDFS upload file " + file.getName() + " error.", e);
        } catch (Exception e) {
            LOGGER.error("FastDFS upload file " + file.getName() + " error.", e);
        } finally {
            IOUtils.closeQuietly(fis);
            /** 上传完毕释放对象 */
            pool.returnTrackServer(trackerServer);
        }
        return null;
    }

    /**
     * 获取FastDFS服务器上的文件的HTTP地址，可以通过该地址直接下载文件。
     *
     * @param fileInfo {@link FdfsFileInfo}
     * @return http格式url
     */
    public String getFileUrl(FdfsFileInfo fileInfo) {
        if (!isFileInfoParamValid(fileInfo)) {
            return EMPTY_STRING;
        }

        TrackerServer trackerServer = null;
        try {
            trackerServer = pool.takeTrackerServer();
            StorageClient client = createStorageClient(trackerServer);

            FileInfo fi = client.get_file_info(fileInfo.getGroupName(), fileInfo.getRemoteFileName());

            StringBuffer url = new StringBuffer("http://");
            url.append(fi.getSourceIpAddr()).append(":").append(port).append("/").append(fileInfo.getGroupName()).append("/").append(fileInfo.getRemoteFileName());
            return url.toString();
        } catch (IOException e) {
            LOGGER.error("Get url for file <" + fileInfo.toString() + "> error.", e);
        } catch (Exception e) {
            LOGGER.error("Get url for file <" + fileInfo.toString() + "> error.", e);
        } finally {
            /** 释放对象 */
            pool.returnTrackServer(trackerServer);
        }
        return EMPTY_STRING;
    }

    /**
     * 从FastDFS服务器上下载文件到本地tomcat服务器
     *
     * @param fileInfo     {@link FdfsFileInfo}
     * @param destPath     本地文件路径。如果不存在会默认使用java临时目录
     * @param destFileName 本地文件名。如果为空，则上传时的文件名作为目标文件名
     * @return 下载后的{@link File}对象
     */
    public File download(FdfsFileInfo fileInfo, String destPath, String destFileName) {
        if (!isFileInfoParamValid(fileInfo)) {
            return null;
        }
        FileOutputStream fos = null;
        TrackerServer trackerServer = null;
        try {
            trackerServer = pool.takeTrackerServer();
            StorageClient client = createStorageClient(trackerServer);

            String groupName = fileInfo.getGroupName();
            String remoteFileName = fileInfo.getRemoteFileName();
            byte[] b = client.download_file(groupName, remoteFileName);
            if (StringUtils.isEmpty(destPath)) {
                destPath = SystemUtils.getJavaIoTmpDir().getAbsolutePath();
            }
            if (StringUtils.isEmpty(destFileName)) {
                NameValuePair[] pairs = client.get_metadata(groupName, remoteFileName);
                for (int i = pairs.length - 1; i >= 0; i++) {// fileName一般会在数组的最后一个，所以这里采用倒序
                    NameValuePair pair = pairs[i];
                    if (StringUtils.equals(pair.getName(), "fileName")) {
                        destFileName = pair.getValue();
                        break;
                    }
                }
            }

            File down = new File(destPath + File.separator + destFileName);
            fos = new FileOutputStream(down);
            fos.write(b);
            LOGGER.info("write file byte length:{}", b.length);
            return down;
        } catch (FileNotFoundException e) {
            LOGGER.error("Uploaded to FastDFS file not found .", e);
        } catch (IOException e) {
            LOGGER.error("FastDFS download file error.", e);
        } catch (Exception e) {
            LOGGER.error("FastDFS error.", e);
        } finally {
            IOUtils.closeQuietly(fos);
            /** 释放对象 */
            pool.returnTrackServer(trackerServer);
        }
        return null;
    }

    /**
     * 删除FastDFS服务器上的文件
     *
     * @param fileInfo {@link FdfsOperator.RemoteFileInfo}
     */
    public void delete(FdfsFileInfo fileInfo) {
        if (!isFileInfoParamValid(fileInfo)) {
            return;
        }

        TrackerServer trackerServer = null;
        try {
            trackerServer = pool.takeTrackerServer();
            StorageClient client = createStorageClient(trackerServer);
            client.delete_file(fileInfo.getGroupName(), fileInfo.getRemoteFileName());
        } catch (IOException e) {
            LOGGER.error("Delete file <" + fileInfo.toString() + "> from FastDFS error", e);
        } catch (Exception e) {
            LOGGER.error("Delete file <" + fileInfo.toString() + "> from FastDFS error", e);
        } finally {
            /** 释放对象 */
            pool.returnTrackServer(trackerServer);
        }

    }

    /**
     * 将流上传到FastDFS，返回URL
     *
     * @param in
     * @param metas
     * @param suffix
     * @return
     */
    public String upload(InputStream in, NameValuePair[] metas, String suffix) {
        TrackerServer trackerServer = null;
        try {
            LOGGER.info("begin upload file by inputStream...");
            trackerServer = pool.takeTrackerServer();
            StorageClient client = createStorageClient(trackerServer);
            byte[] file_buff = IOUtils.toByteArray(in);
            String[] results = client.upload_file(file_buff, suffix, metas);
            String group_name = results[0];
            String remote_filename = results[1];
            FileInfo fi = client.get_file_info(group_name, remote_filename);
            StringBuffer url = new StringBuffer("http://");
            url.append(fi.getSourceIpAddr()).append(":").append(port).append("/").append(group_name).append("/").append(remote_filename);
            LOGGER.info("upload file by inputStream success!");
            return url.toString();
        } catch (IOException e) {
            LOGGER.error("upload Stream error.", e);
        } catch (Exception e) {
            LOGGER.error("upload Stream error.", e);
        } finally {
            /** 释放对象 */
            pool.returnTrackServer(trackerServer);
        }
        return null;
    }


    /**
     * 创建StorageClient
     *
     * @param trackerServer
     * @return
     */
    private StorageClient createStorageClient(TrackerServer trackerServer) {
        StorageServer storageServer = null;
        StorageClient client = new StorageClient(trackerServer, storageServer);
        return client;
    }

    /**
     * 对参数做校验
     *
     * @param fileInfo {@link FdfsOperator.RemoteFileInfo}
     * @return
     */
    private boolean isFileInfoParamValid(FdfsFileInfo fileInfo) {
        if (fileInfo == null || StringUtils.isEmpty(fileInfo.getGroupName()) || StringUtils.isEmpty(fileInfo.getRemoteFileName())) {
            LOGGER.error("null is not a fileInfo value.", new IllegalArgumentException("null is not a fileInfo value."));
            return false;
        }
        return true;
    }

    /**
     * 上传文件返回的文件基本信息，包含远程文件的文件名以及远程文件的组名
     *
     * @author wtian
     */
    static class FdfsFileInfo implements Serializable {
        /**
         * 远程文件组名
         */
        private String groupName;

        /**
         * 远程文件文件名
         */
        private String remoteFileName;

        public FdfsFileInfo(String groupName, String remoteFileName) {
            super();
            this.groupName = groupName;
            this.remoteFileName = remoteFileName;
        }

        public String getGroupName() {
            return groupName;
        }

        public void setGroupName(String groupName) {
            this.groupName = groupName;
        }

        public String getRemoteFileName() {
            return remoteFileName;
        }

        public void setRemoteFileName(String remoteFileName) {
            this.remoteFileName = remoteFileName;
        }

    }


}
